﻿using System;
using Tao.OpenGl;
using System.Collections.Generic;

namespace Aeon.Framework
{

/// <summary>
/// Dynamic mesh skydome object class.
/// 
/// </summary>
public class Skydome : IObject
    {
    // --- Fields:
    
    private AeonColor color;            // Vertex color.
    private ITexture texture;           // Texture image.
    private Vertex[] vertexes;          // Dome vertex list.
    
    // --- Constructor:
    
    public Skydome(String key, Position center, Orientation face, UInt16 radius, UInt16 lod, AeonColor color, ITexture texture)
        : base(key,center,face)
        {
        this.color = color;
        this.texture = texture;
        
        vertexes = new Vertex[((360/lod)*(90/lod)*4)];
        
        ushort vertices = 0;
        const double radians = Math.PI/180;
        for (ushort phi=0;phi<=90-lod;phi+=lod)
            {
            for (ushort theta=0;theta<=360-lod;theta+=lod)
                {
                float vx, vy, vz, versor;
                
                vertexes[vertices].X = (float)(radius*Math.Sin(phi*radians)*Math.Cos(radians*theta));
                vertexes[vertices].Y = (float)(radius*Math.Sin(phi*radians)*Math.Sin(radians*theta));
                vertexes[vertices].Z = (float)(radius*Math.Cos(phi*radians));

                vx = vertexes[vertices].X;
                vy = vertexes[vertices].Y;
                vz = vertexes[vertices].Z;

                versor = (float)Math.Sqrt((vx*vx)+(vy*vy)+(vz*vz));
                vx /= versor;
                vy /= versor;
                vz /= versor;

                vertexes[vertices].U = (float)(Math.Atan2(vx,vz)/(Math.PI*2))+0.5f;
                vertexes[vertices].V = (float)(Math.Asin(vy)/Math.PI)+0.5f;
                
                vertexes[vertices].Color = color;
                vertices++;

                vertexes[vertices].X = (float)(radius*Math.Sin((phi+lod)*radians)*Math.Cos(theta*radians));
                vertexes[vertices].Y = (float)(radius*Math.Sin((phi+lod)*radians)*Math.Sin(theta*radians));
                vertexes[vertices].Z = (float)(radius*Math.Cos((phi+lod)*radians));

                vx = vertexes[vertices].X;
                vy = vertexes[vertices].Y;
                vz = vertexes[vertices].Z;

                versor = (float)Math.Sqrt((vx*vx)+(vy*vy)+(vz*vz));
                vx /= versor;
                vy /= versor;
                vz /= versor;

                vertexes[vertices].U = (float)(Math.Atan2(vx,vz)/(Math.PI*2))+0.5f;
                vertexes[vertices].V = (float)(Math.Asin(vy)/Math.PI)+0.5f;

                vertexes[vertices].Color = color;
                vertices++;

                vertexes[vertices].X = (float)(radius*Math.Sin(radians*phi)*Math.Cos(radians*(theta+lod)));
                vertexes[vertices].Y = (float)(radius*Math.Sin(radians*phi)*Math.Sin(radians*(theta+lod)));
                vertexes[vertices].Z = (float)(radius*Math.Cos(radians*phi));

                vx = vertexes[vertices].X;
                vy = vertexes[vertices].Y;
                vz = vertexes[vertices].Z;

                versor = (float)Math.Sqrt((vx*vx)+(vy*vy)+(vz*vz));
                vx /= versor;
                vy /= versor;
                vz /= versor;

                vertexes[vertices].U = (float)(Math.Atan2(vx,vz)/(Math.PI*2))+0.5f;
                vertexes[vertices].V = (float)(Math.Asin(vy)/Math.PI)+0.5f;

                vertexes[vertices].Color = color;
                vertices++;

                if (phi<90)
                    {
                    vertexes[vertices].X = (float)(radius*Math.Sin((phi+lod)*radians)*Math.Cos(radians*(theta+lod)));
                    vertexes[vertices].Y = (float)(radius*Math.Sin((phi+lod)*radians)*Math.Sin(radians*(theta+lod)));
                    vertexes[vertices].Z = (float)(radius*Math.Cos((phi+lod)*radians));

                    vx = vertexes[vertices].X;
                    vy = vertexes[vertices].Y;
                    vz = vertexes[vertices].Z;

                    versor = (float)Math.Sqrt((vx*vx)+(vy*vy)+(vz*vz));
                    vx /= versor;
                    vy /= versor;
                    vz /= versor;

                    vertexes[vertices].U = (float)(Math.Atan2(vx,vz)/(Math.PI*2))+0.5f;
                    vertexes[vertices].V = (float)(Math.Asin(vy)/Math.PI)+0.5f;

                    vertexes[vertices].Color = color;
                    vertices++;
                    }
                }
            }

        // Dateline clamping fix.
        // [TODO] Polar bunching fix.
        for (ushort v=0;v<vertices-3;v++)
            {
            if (vertexes[v].V-vertexes[v+1].V>0.8f) vertexes[v+1].V+=1.0f;
            if (vertexes[v].U-vertexes[v+1].U>0.9f) vertexes[v+1].U+=1.0f;
            if (vertexes[v].U-vertexes[v+2].U>0.9f) vertexes[v+2].U+=1.0f;
            if (vertexes[v].V-vertexes[v+2].V>0.8f) vertexes[v+2].V+=1.0f;

            if (vertexes[v+1].V-vertexes[v].V>0.8f) vertexes[v].V+=1.0f;
            if (vertexes[v+1].U-vertexes[v].U>0.9f) vertexes[v].U+=1.0f;
            if (vertexes[v+1].V-vertexes[v+2].V>0.8f) vertexes[v+2].V+=1.0f;
            if (vertexes[v+1].U-vertexes[v+2].U>0.9f) vertexes[v+2].U+=1.0f;

            if (vertexes[v+2].V-vertexes[v].V>0.8f) vertexes[v].V+=1.0f;
            if (vertexes[v+2].U-vertexes[v].U>0.9f) vertexes[v].U+=1.0f;
            if (vertexes[v+2].V-vertexes[v+1].V>0.8f) vertexes[v+1].V+=1.0f;
            if (vertexes[v+2].U-vertexes[v+1].U>0.9f) vertexes[v+1].U+=1.0f;
            }
            
        }

    // --- External Methods:

    public override void Draw()                 // [TODO] Optimize with Display Lists?
        {
        Gl.glTranslated(origin.xPos,origin.yPos,origin.zPos);
        Gl.glRotated(face.xAng+270,1,0,0);
        Gl.glRotated(face.yAng,0,1,0);
        Gl.glRotated(face.zAng,0,0,1);
        
        texture.Bind();

        Gl.glDisable(Gl.GL_CULL_FACE);          // [TODO] Test for active face culling? Or invert normals?
        Gl.glBegin(Gl.GL_TRIANGLE_STRIP);       // Polygons.
        //Gl.glBegin(Gl.GL_LINES);              // Wireframe.
        
        for (int cnt = vertexes.Length-1; cnt >= 0; cnt--)
            {
            Vertex v = vertexes[cnt];

            Gl.glColor4f(v.Color.R,v.Color.G,v.Color.B,v.Color.A);

            Gl.glTexCoord2f(v.U,v.V);
            Gl.glVertex3f(v.X,v.Y,v.Z);
            }

        Gl.glEnd();
        Gl.glEnable(Gl.GL_CULL_FACE);
        }

    }


}